import {isActiveFeature, isInactiveFeature, isOfMetaType, isShiftDown, noTarget} from '../lib/common_selectors';
import createSupplementaryPoints from '../lib/create_supplementary_points';
import constrainFeatureMovement from '../lib/constrain_feature_movement';
import doubleClickZoom from '../lib/double_click_zoom';
import * as Constants from '../constants';
import moveFeatures from '../lib/move_features';

import createVertex from '../lib/create_vertex'
import DrawUtil from '../util/DrawUtils'

const isVertex = isOfMetaType(Constants.meta.VERTEX);
const isMidpoint = isOfMetaType(Constants.meta.MIDPOINT);

const DirectSelect = {};

// INTERNAL FUCNTIONS

DirectSelect.fireUpdate = function () {
    this.map.fire(Constants.events.UPDATE, {
        action: Constants.updateActions.CHANGE_COORDINATES,
        features: this.getSelected().map(f => f.toGeoJSON())
    });
};

DirectSelect.fireActionable = function (state) {
    this.setActionableState({
        combineFeatures: false,
        uncombineFeatures: false,
        trash: state.selectedCoordPaths.length > 0
    });
};

DirectSelect.startDragging = function (state, e) {
    this.map.dragPan.disable();
    state.canDragMove = true;
    state.dragMoveLocation = e.lngLat;
};

DirectSelect.stopDragging = function (state) {
    this.map.dragPan.enable();
    state.dragMoving = false;
    state.canDragMove = false;
    state.dragMoveLocation = null;
};

DirectSelect.onVertex = function (state, e) {
    this.startDragging(state, e);
    const about = e.featureTarget.properties;
    const selectedIndex = state.selectedCoordPaths.indexOf(about.coord_path);
    if (!isShiftDown(e) && selectedIndex === -1) {
        state.selectedCoordPaths = [about.coord_path];
    } else if (isShiftDown(e) && selectedIndex === -1) {
        state.selectedCoordPaths.push(about.coord_path);
    }

    const selectedCoordinates = this.pathsToCoordinates(state.featureId, state.selectedCoordPaths);
    this.setSelectedCoordinates(selectedCoordinates);
};

DirectSelect.onMidpoint = function (state, e) {
    this.startDragging(state, e);
    const about = e.featureTarget.properties;
    state.feature.addCoordinate(about.coord_path, about.lng, about.lat);
    this.fireUpdate();
    state.selectedCoordPaths = [about.coord_path];
};

DirectSelect.pathsToCoordinates = function (featureId, paths) {
    return paths.map(coord_path => ({feature_id: featureId, coord_path}));
};

DirectSelect.onFeature = function (state, e) {
    if (state.selectedCoordPaths.length === 0) this.startDragging(state, e);
    else this.stopDragging(state);
};

DirectSelect.dragFeature = function (state, e, delta) {
    moveFeatures(this.getSelected(), delta);
    state.dragMoveLocation = e.lngLat;
    state.dragMoveLocation = e.lngLat;
};

DirectSelect.dragVertex = function (state, e, delta) {
    let splitElement = state.selectedCoordPaths[0].split('.')[1];
    const points = state.feature.properties.points;
    if (points) {
        let type = state.feature.properties.type;
        let tempArg = state.feature.properties.tempArg;
        points[splitElement] = [e.lngLat.lng, e.lngLat.lat];
        state.feature.incomingCoords(DrawUtil.drawPlot(type, points,tempArg));

    } else {
        const selectedCoords = state.selectedCoordPaths.map(coord_path => state.feature.getCoordinate(coord_path));
        const selectedCoordPoints = selectedCoords.map(coords => ({
            type: Constants.geojsonTypes.FEATURE,
            properties: {},
            geometry: {
                type: Constants.geojsonTypes.POINT,
                coordinates: coords
            }
        }));

        const constrainedDelta = constrainFeatureMovement(selectedCoordPoints, delta);
        for (let i = 0; i < selectedCoords.length; i++) {
            const coord = selectedCoords[i];
            state.feature.updateCoordinate(state.selectedCoordPaths[i], coord[0] + constrainedDelta.lng, coord[1] + constrainedDelta.lat);
        }
    }
};

DirectSelect.clickNoTarget = function () {
    this.changeMode(Constants.modes.SIMPLE_SELECT);
};

DirectSelect.clickInactive = function () {
    this.changeMode(Constants.modes.SIMPLE_SELECT);
};

DirectSelect.clickActiveFeature = function (state) {
    state.selectedCoordPaths = [];
    this.clearSelectedCoordinates();
    state.feature.changed();
};

// EXTERNAL FUNCTIONS

DirectSelect.onSetup = function (opts) {
    const featureId = opts.featureId;
    const feature = this.getFeature(featureId);

    if (!feature) {
        throw new Error('You must provide a featureId to enter direct_select mode');
    }

    if (feature.type === Constants.geojsonTypes.POINT) {
        throw new TypeError('direct_select mode doesn\'t handle point features');
    }

    const state = {
        featureId,
        feature,
        dragMoveLocation: opts.startPos || null,
        dragMoving: false,
        canDragMove: false,
        selectedCoordPaths: opts.coordPath ? [opts.coordPath] : []
    };

    this.setSelectedCoordinates(this.pathsToCoordinates(featureId, state.selectedCoordPaths));
    this.setSelected(featureId);
    doubleClickZoom.disable(this);

    this.setActionableState({
        trash: true
    });

    return state;
};

DirectSelect.onStop = function () {
    doubleClickZoom.enable(this);
    this.clearSelectedCoordinates();
};

DirectSelect.toDisplayFeatures = function (state, geojson, push) {
    if (state.featureId === geojson.properties.id) {
        geojson.properties.active = Constants.activeStates.ACTIVE;
        push(geojson);
        let points = this.getFeature(geojson.properties.id).properties.points;

        const supplementaryPoints = points ? createSupplementaryThroughPoints(points, geojson.properties.id) : createSupplementaryPoints(geojson, {
            map: this.map,
            midpoints: true,
            selectedPaths: state.selectedCoordPaths
        });

        supplementaryPoints.forEach(push);


    } else {
        geojson.properties.active = Constants.activeStates.INACTIVE;
        push(geojson);
    }
    this.fireActionable(state);

    function createSupplementaryThroughPoints(points, featureId) {

        const supplementaryPoints = [];

        for (let index = 0; index < points.length; index++) {
            supplementaryPoints.push(createVertex(featureId, points[index], `0.${index}`, false))
        }
        return supplementaryPoints;
    }

};

DirectSelect.onTrash = function (state) {
    // Uses number-aware sorting to make sure '9' < '10'. Comparison is reversed because we want them
    // in reverse order so that we can remove by index safely.
    state.selectedCoordPaths
        .sort((a, b) => b.localeCompare(a, 'en', {numeric: true}))
        .forEach(id => state.feature.removeCoordinate(id));
    this.fireUpdate();
    state.selectedCoordPaths = [];
    this.clearSelectedCoordinates();
    this.fireActionable(state);
    if (state.feature.isValid() === false) {
        this.deleteFeature([state.featureId]);
        this.changeMode(Constants.modes.SIMPLE_SELECT, {});
    }
};

DirectSelect.onMouseMove = function (state, e) {
    // On mousemove that is not a drag, stop vertex movement.
    const isFeature = isActiveFeature(e);
    const onVertex = isVertex(e);
    const noCoords = state.selectedCoordPaths.length === 0;
    if (isFeature && noCoords) this.updateUIClasses({mouse: Constants.cursors.MOVE});
    else if (onVertex && !noCoords) this.updateUIClasses({mouse: Constants.cursors.MOVE});
    else this.updateUIClasses({mouse: Constants.cursors.NONE});
    this.stopDragging(state);

    // Skip render
    return true;
};

DirectSelect.onMouseOut = function (state) {
    // As soon as you mouse leaves the canvas, update the feature
    if (state.dragMoving) this.fireUpdate();

    // Skip render
    return true;
};

DirectSelect.onTouchStart = DirectSelect.onMouseDown = function (state, e) {


    if (isVertex(e)) return this.onVertex(state, e);
    if (isActiveFeature(e)) return this.onFeature(state, e);
    if (isMidpoint(e)) return this.onMidpoint(state, e);
};

DirectSelect.onDrag = function (state, e) {
    if (state.canDragMove !== true) return;
    state.dragMoving = true;
    e.originalEvent.stopPropagation();

    const delta = {
        lng: e.lngLat.lng - state.dragMoveLocation.lng,
        lat: e.lngLat.lat - state.dragMoveLocation.lat
    };
    if (state.selectedCoordPaths.length > 0) this.dragVertex(state, e, delta);
    else this.dragFeature(state, e, delta);

    state.dragMoveLocation = e.lngLat;
};

DirectSelect.onClick = function (state, e) {
    if (noTarget(e)) return this.clickNoTarget(state, e);
    if (isActiveFeature(e)) return this.clickActiveFeature(state, e);
    if (isInactiveFeature(e)) return this.clickInactive(state, e);
    this.stopDragging(state);
};

DirectSelect.onTap = function (state, e) {
    if (noTarget(e)) return this.clickNoTarget(state, e);
    if (isActiveFeature(e)) return this.clickActiveFeature(state, e);
    if (isInactiveFeature(e)) return this.clickInactive(state, e);
};

DirectSelect.onTouchEnd = DirectSelect.onMouseUp = function (state) {
    if (state.dragMoving) {
        this.fireUpdate();
    }
    this.stopDragging(state);
};


export default DirectSelect;

